package org.nlp2rdf.navigator.benchmark;

import com.google.common.collect.Sets;
import org.aksw.commons.util.collections.PrefetchIterator;
import org.aksw.commons.util.random.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * Created by Claus Stadler
 * Date: Oct 15, 2010
 * Time: 4:57:29 PM
 */
@Deprecated
public class PhaseIterator<E, D>
        implements Iterator<PhaseContext<E, D>> {
    private static final Logger logger = LoggerFactory.getLogger(PhaseIterator.class);

    private Sample<E> all;
    private Sample<E> current;
    private Sample<E> pool;
    private IGenericLearner<E, D> learner;
    private IGenericClassifier<E, D> classifier;

    private int phaseId = 0;
    private Random random;

    //private Set<E> nextPositives = Collections.emptySet();
    //private Set<E> nextNegatives = Collections.emptySet();
    private Sample<E> next = Sample.create();


    public Sample<E> getCurrent() {
        return current;
    }

    public Sample<E> getNext() {
        return Sample.create(
                Sets.union(next.getPositives(), current.getPositives()),
                Sets.union(next.getNegatives(), current.getNegatives()));
    }

    /**
     * @param all        All positives and all negatives. all.negatives is ignored.
     * @param pool       The pool of positives and negatives from which can be drawn
     * @param init       The initial training sample
     * @param random
     * @param learner
     * @param classifier
     */
    public PhaseIterator(Sample<E> all, Sample<E> pool, Sample<E> init, Random random, IGenericLearner<E, D> learner, IGenericClassifier<E, D> classifier) {
        this.all = all;
        this.pool = pool;
        this.current = Sample.create();
        this.next = Sample.createCopy(init);
        //this.current = Sample.createCopy(init);
        this.random = random;
        this.learner = learner;
        this.classifier = classifier;
    }


    @Override
    public PhaseContext<E, D> next() {
        try {
            return nextInteral();
        } catch (Exception e) {
            logger.error("Error in iterator", e);
            return null;
        }
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException();
    }

    public boolean hasNext() {
        return true;
    }


    protected PhaseContext<E, D> nextInteral()
            throws Exception {
        //logger.info("----------------------------");
        logger.trace("Entering phase " + phaseId);


        current.addAll(next);
        //current.getPositives().addAll(nextPositives);
        //current.getNegatives().addAll(nextNegatives);


        //logger.info("All current positives: " + current.getPositives().size());
        //logger.info("All current negatives: " + current.getNegatives().size());
        //logger.info("Positives[" + phaseId + "]: (" + current.getPositives().size() + ") " + current.getPositives());
        //logger.info("Negatives[" + phaseId + "]: (" + current.getNegatives().size() + ") " + current.getNegatives());


        //assert all.getPositives().containsAll(current.getPositives());
        //assert all.getNegatives().containsAll(current.getNegatives());
        //assert Sets.intersection(candidate.getPositives(), candidate.getNegatives()).isEmpty();
        //assert Sets.intersection(candidate.getNegatives(), candidate.getPositives()).isEmpty();

        Set<E> examples;
        D description;
        long learnDuration;
        long classifyDuration;
            //EvaluatedDescription description = learn(current);
            long learnStart = System.nanoTime();
            try {
                description = learner.learn(current);
            }catch (Exception e) {
                logger.error("Error during learning", e);
                return null;
            }
            learnDuration = System.nanoTime() - learnStart;

            if (description == null)
                return null;

            long classifyStart = System.nanoTime();
            try {
                examples = classifier.classify(description);
                classifyDuration = System.nanoTime() - classifyStart;
            } catch (Exception e) {
                logger.error("Error during retrieval for concept " + description, e);
                return null;
            }

            if (examples == null)
                return null;

        //logger.info(description.getDescription());

        //Set<Resource> examples = retrieveExamples(description);


        Set<E> poolTruePositives = Sets.intersection(pool.getPositives(), examples);
        Set<E> falsePositives = Sets.difference(examples, all.getPositives());

        //logPhase(context, phaseId, duration, , negatives, description, examples, truePositives, falsePositives);
        //logPhase(id, phaseId, duration, all, current, description, examples);
        PhaseContext<E, D> phaseContext = new PhaseContext<E, D>(phaseId, pool, Sample.createCopy(current), description, new HashSet<E>(examples), learnDuration, classifyDuration);

        Set<E> nextPositives = Sets.difference(poolTruePositives, current.getPositives());
        Set<E> nextNegatives = Sets.difference(falsePositives, current.getNegatives());


        next = Sample.create(
                RandomUtils.randomSampleSet(nextPositives, 5, random),
                RandomUtils.randomSampleSet(nextNegatives, 5, random));

        // FIXME Make the computation of nextPos/Neg configurable
        //nextPositives = RandomUtils.randomSampleSet(nextPositives, 5, random);
        //nextNegatives = RandomUtils.randomSampleSet(nextNegatives, 5, random);


        ++phaseId;
        return phaseContext;
    }
}